
/*
This file contains the main code for the algorithm proposed in the following paper:

Qiang Yang, Wei-Neng Chen, Jeremiah D. Deng, Yun Li, Tianlong Gu and Jun Zhang, "Level-based Learning Swarm Optimizer for Large Scale Optimization",
IEEE Transactions on Evolutionary Computation, conditionally accepted, 2017.


*/

#include "./CEC2010/Header.h"
#include "Self_Define_Functions.h"
#include <sys/time.h>
#include <cstdio>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <iomanip>
#include <string>
#include <boost/random.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/cauchy_distribution.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/uniform_real.hpp>
#include <time.h>


using namespace std;


int main(int argc, char *argv[])
{


    Benchmarks *fp = NULL;

    int funToRun[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};  //function set
    int funNum = 20; //the total number of functions to be optimized in CEC'2010 benchmark set

    int i,j,k,t;

    int gl_best = 0;

    double *result_all_run = new double[timesOfRun]; // to record the global best fitness of each run
    double *time_results = new double[timesOfRun]; // to record the total time of each run


    double *final_global_best = new double[ dim ]; // the final global best solution
    double final_val;
    double pre_final_val;


    double **population = new double*[ Population_size ];
    double **speed = new double*[ Population_size ];
    for( i =0; i < Population_size; ++ i )
    {
        population[i] = new double [dim];
        speed[i] = new double [dim];
    }

    double *results = new double[ Population_size ];// the fitness results for the whole population


    int level_size;
    int last_level_size;
    int temp_size;


    double *record_for_levels = new double[rand_level_size];
    double *probablity_for_levels = new double[rand_level_size];


    int FV = 0;

    int selected_particle1, selected_particle2, selected_level1, selected_level2;


    int level_num_index;
    int level_num;

    clock_t start,finish;

    int **levels  = new int *[Population_size];//0 the first level, 1 the second level ....LEVEL_NUM-1 the LEVEL_NUM level
    for ( i = 0; i < Population_size; ++i )
    {
            levels[i] = new int[Population_size];
    }


    for ( k=0; k<funNum; k++)
    {
        cout<<"Function "<<funToRun[k]<<" Begins!"<<endl;

        fp = generateFuncObj(funToRun[k]);

        FV = 0;

        boost::mt19937 generator(time(0)*rand());

        // to initialize the population
        boost::uniform_real<> uniform_real_generate_x( fp->getMinX(), fp->getMaxX() );
        boost::variate_generator< boost::mt19937&, boost::uniform_real<> > random_real_num_x( generator, uniform_real_generate_x );

        boost::uniform_real<> uniform_real_generate_r( 0, 1 );
        boost::variate_generator< boost::mt19937&, boost::uniform_real<> > random_real_num_r( generator, uniform_real_generate_r );


        //write the results into files
        char fun[10];
        snprintf(fun,10,"%d",funToRun[k]);
        string filename_fitness = "./Results/Fitness_result_for_function_"+ string(fun)+".txt";
        ofstream out_fitness(filename_fitness.c_str());


        if(!out_fitness)
        {
            cerr<<"Can not open the file " <<filename_fitness<<endl;
            exit(1);
        }

        for (t=0; t < timesOfRun; t++)
        {
                cout<<"Running the "<<t<<"th run."<<endl;

                start = clock();
                FV = 0;

                // initialize the population
                for( i =0 ; i < Population_size; ++i )
                    for( j =0; j < dim; ++j )
                    {
                        population[i][j] = random_real_num_x();
                        speed[i][j] = 0;
                    }

                Fitness_Computation( results, population, gl_best, Population_size, dim, FV, fp );
                pre_final_val = final_val = results[gl_best];
                memcpy( final_global_best, population[gl_best], sizeof(double)*dim );

                for( i = 0; i < rand_level_size; ++i )
                {
                    record_for_levels[i] = 1;
                }

                while( FV < MAX_FV )
                {
                    //compute the probability of each level number
                    Compute_Probablity( record_for_levels, probablity_for_levels, rand_level_size );

                    // randomly select a number from the pool as the level number
                    level_num_index =  Select_Level_Num( probablity_for_levels, rand_level_size );
                    level_num = rand_level_set[level_num_index];

                    // compute the level size of each level and the last level
                    level_size = Population_size / level_num;
                    last_level_size = level_size + Population_size % level_num;

                    //sort the swarm
                    Ranking( results, levels, level_num, level_size,last_level_size, Population_size );

                    //update particles from the lowest level to the third level
                    for( i = level_num-1; i > 1; --i )
                    {
                        boost::mt19937 generator2(time(0)*rand());
                        boost::uniform_int<> uniform_int_particle( 0, level_size-1 );
                        boost::variate_generator< boost::mt19937&, boost::uniform_int<> > random_int_particle( generator2, uniform_int_particle );

                        boost::uniform_int<> uniform_int_level( 0,  i-1 );
                        boost::variate_generator< boost::mt19937&, boost::uniform_int<> > random_int_level( generator2, uniform_int_level );

                        // whether this level is the lowest level
                        if( i == level_num-1 )
                            temp_size = last_level_size;
                        else
                            temp_size = level_size;

                        for( j = 0; j < temp_size; ++j )
                        {
                            //randomly select two levels
                            selected_level1 = random_int_level();
                            selected_level2 = random_int_level();

                            //keep that the two selected levels are different
                            while( selected_level2 == selected_level1 )
                            {
                                selected_level2 = random_int_level();
                            }

                            //swap the two selected levels if the second level is higher than the first; note that the smaller the level index, the higher the level
                            if( selected_level1 > selected_level2 )
                            {
                                swap( selected_level1, selected_level2);
                            }

                            //randomly select one particle from each selected level
                            selected_particle2 = random_int_particle();
                            selected_particle1 = random_int_particle();

                            //update particles
                            Update_Particle( population[levels[i][j]], speed[levels[i][j]], population[levels[selected_level1][selected_particle1]], population[levels[selected_level2][selected_particle2]], dim, phi, fp );
                            Fitness( results[levels[i][j]], population[levels[i][j]], FV, fp );

                            //update the global fitness
                            if( results[levels[i][j]] < final_val )
                            {
                                final_val = results[levels[i][j]];
                                memcpy( final_global_best, population[levels[i][j]], sizeof(double)*dim );
                            }
                        }

                    }

                    boost::mt19937 generator3(time(0)*rand());
                    boost::uniform_int<> uniform_int_particle2( 0, level_size-1 );
                    boost::variate_generator< boost::mt19937&, boost::uniform_int<> > random_int_particle2( generator3, uniform_int_particle2 );


                    //update the second level
                    for( j = 0; j < level_size; ++j )
                    {
                            //randomly select two different particles from the first level
                            selected_particle1 = random_int_particle2();

                            do
                            {
                                selected_particle2 = random_int_particle2();
                            }while( selected_particle1 == selected_particle2 );

                            if( results[levels[0][selected_particle2]] < results[levels[0][selected_particle1]])
                                swap( selected_particle1, selected_particle2 );


                            //update particles
                            Update_Particle( population[levels[i][j]], speed[levels[i][j]], population[levels[0][selected_particle1]], population[levels[0][selected_particle2]], dim, phi, fp );
                            Fitness( results[levels[i][j]], population[levels[i][j]], FV, fp );

                            //update the global fitness
                            if( results[levels[i][j]] < final_val )
                            {
                                final_val = results[levels[i][j]];
                                memcpy( final_global_best, population[levels[i][j]], sizeof(double)*dim );
                            }
                    }

                    //record the relative performance improvement of the selected level number
                    record_for_levels[level_num_index] = Compute_Relative_Performance( pre_final_val, final_val );

                    pre_final_val = final_val;

                }

            finish = clock();
            result_all_run[t] = final_val;

            time_results[t] = double (finish - start ) / CLOCKS_PER_SEC;

            cout<<"The "<<t<<"th rum: Fitness :"<<result_all_run[t]<<"\t"<<" time: "<<time_results[t]<<endl;
        }

        //ouput the results to files
        for( i = 0; i < timesOfRun; ++i )
        {
            out_fitness<<result_all_run[i]<<"\t"<<time_results[i]<<endl;
        }


        out_fitness.close();

        cout<<"Function "<<funToRun[k]<<" Finishes."<<endl;

	}


	//release the resouces
    for( i =0; i < Population_size; ++ i )
    {
        delete []population[i];

        delete []speed[i];
    }
    delete []population;
    delete []speed;


    delete []result_all_run;
    delete []time_results;

    delete []final_global_best;
    delete []results;

    for( i = 0; i < Population_size; ++i )
    {
        delete []levels[i];
    }

    delete []levels;


    return 0;
}



